// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#include <asm.h>
#include <err.h>

/* Register use in this code:
 * %rdi = argument 1, void* dst
 * %rsi = argument 2, const void* src
 * %rdx = argument 3, size_t len
 *   - moved to %rcx
 * %rcx = argument 4, void** fault_return
 *   - moved to %r10
 */

# status_t _x86_copy_to_or_from_user(void *dst, const void *src, size_t len, void **fault_return)
FUNCTION(_x86_copy_to_or_from_user)
    # Copy fault_return out of %rcx, because %rcx is used by "rep movsb" later.
    movq %rcx, %r10

    # Check if SMAP is enabled
    cmpb $0, g_x86_feature_smap(%rip)
    # Disable SMAP protection if SMAP is enabled
    jz 0f
    stac
0:

    # Setup page fault return
    movq $.Lfault_copy, (%r10)

    # Between now and the reset of the fault return, we cannot make a function
    # call or manipulate the stack.  We need to be able to restore all callee
    # registers, without any knowledge of where between these two points we
    # faulted.

    # Perform the actual copy
    cld
    # %rdi and %rsi already contain the destination and source addresses.
    movq %rdx, %rcx
    rep movsb  # while (rcx-- > 0) *rdi++ = *rsi++;

    mov $ZX_OK, %rax

.Lcleanup_copy:
    # Reset fault return
    movq $0, (%r10)

    # Re-enable SMAP protection
    cmpb $0, g_x86_feature_smap(%rip)
    jz 0f
    clac
0:
    ret

.Lfault_copy:
    mov $ZX_ERR_INVALID_ARGS, %rax
    jmp .Lcleanup_copy
END_FUNCTION(_x86_copy_to_or_from_user)
